package com.koudai.finance.utils;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.koudai.finance.constant.SysCode;

import com.koudai.finance.entity.User;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.UUID;

/**
 * @Description: jwtToken生成工具类
 * @author: ls
 * @Date: 2020/7/15 14:47
 */
@Slf4j
public class JwtTokenUtil {

    /**
     * 组装生成用户token
     *
     * @return
     */
    public static String getExpToken(User user) {
        Map<String, Object> claimMaps = Maps.newHashMap();
        claimMaps.put(SysCode.LOGIN_USER, user);
        return getTokenByJson(claimMaps, SysCode.TOKEN_SECRET_KEY, SysCode.TOKEN_TIMEOUT);
    }

    /**
     * @Description: 根据userId组装生成用户token
     * @author: ls
     * @Date: 2020/7/16 9:50
     */
    public static String getExpToken(Integer userId) {
        Map<String, Object> claimMaps = Maps.newHashMap();
        claimMaps.put(SysCode.LOGIN_USER, userId);
        return getTokenByJson(claimMaps, SysCode.TOKEN_SECRET_KEY, SysCode.TOKEN_TIMEOUT);
    }

    /**
     * 获取token - json化 map信息
     *
     * @param claimMaps
     * @param secretKey
     * @param secondTimeOut
     * @return
     */
    public static String getTokenByJson(Map<String, Object> claimMaps, String secretKey, long secondTimeOut) {
        return getToken(claimMaps, true, secretKey, secondTimeOut);
    }

    /**
     * 获取token
     * @param claimMaps
     * @param isJsonMap
     * @param secretKey
     * @param secondTimeOut
     * @return
     */
    public static String getToken(Map<String, Object> claimMaps, boolean isJsonMap, String secretKey, long secondTimeOut) {
        if (isJsonMap) {
            claimMaps.forEach((key, val) -> claimMaps.put(key, JSON.toJSONString(val)));
        }
        long currentTime = System.currentTimeMillis();
        long futureTime = currentTime + secondTimeOut * 1000;
        /* iss: jwt签发者
           sub: jwt所面向的用户
           aud: 接收jwt的一方
           exp: jwt的过期时间，这个过期时间必须要大于签发时间
           nbf: 定义在什么时间之前，该jwt都是不可用的.
           iat: jwt的签发时间
           jti: jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击。
         */
        return Jwts.builder()
                .setId(UUID.randomUUID().toString())
                // 签发时间
                .setIssuedAt(new Date(currentTime))
                // 代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .setSubject("system")
                // 签发者信息
                .setIssuer("finance")
                // 接收用户
                .setAudience("custom")
                //数据压缩方式
                .compressWith(CompressionCodecs.GZIP)
                //加密方式
                .signWith(SignatureAlgorithm.HS256, secretKey)
                //过期时间戳
                .setExpiration(new Date(futureTime))
                //cla信息
                .addClaims(claimMaps)
                .compact();
    }

    /**
     * 获取token中的claims信息
     *
     * @param token
     * @param secretKey
     * @return
     */
    private static Jws<Claims> getJws(String token, String secretKey) {
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token);
    }

    /**
     * 获取签名
     *
     * @param token
     * @param secretKey
     * @return
     */
    public static String getSignature(String token, String secretKey) {
        try {
            return getJws(token, secretKey).getSignature();
        } catch (Exception ex) {
            return "";
        }
    }

    /**
     * 获取token中head信息
     *
     * @param token
     * @param secretKey
     * @return
     */
    public static JwsHeader getHeader(String token, String secretKey) {
        try {
            return getJws(token, secretKey).getHeader();
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * 获取payload body信息
     *
     * @param token
     * @param secretKey
     * @return
     */
    public static Claims getClaimsBody(String token, String secretKey) {
        return getJws(token, secretKey).getBody();
    }

    /**
     * 获取body某个值
     *
     * @param token
     * @param secretKey
     * @param key
     * @return
     */
    public static Object getVal(String token, String secretKey, String key) {
        return getJws(token, secretKey).getBody().get(key);
    }

    /**
     * 获取body某个值，json字符转实体
     *
     * @param token
     * @param secretKey
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getValByT(String token, String secretKey, String key, Class<T> clazz) {
        try {
            String strJson = getVal(token, secretKey, key).toString();
            return JSON.parseObject(strJson, clazz);
        } catch (Exception ex) {
            log.error("parse token failed,token={}", token, ex);
            return null;
        }
    }

    /**
     * 是否过期
     *
     * @param token
     * @param secretKey
     * @return
     */
    public static boolean isExpiration(String token, String secretKey) {
        try {
            return getClaimsBody(token, secretKey)
                    .getExpiration()
                    .before(new Date());
        } catch (ExpiredJwtException ex) {
            return true;
        }
    }


    /**
     * 过期时间
     *
     * @param token
     * @param secretKey
     * @return
     */
    public static Date getExpDate(String token, String secretKey) {
        try {
            return getClaimsBody(token, secretKey)
                    .getExpiration();
        } catch (ExpiredJwtException ex) {
            return null;
        }
    }

    /**
     * 获取subject
     *
     * @param token
     * @param secretKey
     * @return
     */
    public static String getSubject(String token, String secretKey) {
        try {
            return getClaimsBody(token, secretKey).getSubject();
        } catch (Exception ex) {
            log.error("invalid token,token={}", token);
            return "";
        }
    }

    public static void main(String[] args) throws InterruptedException {
        String token = getExpToken(1);
        System.out.println(token);
    }
}
